﻿using System;
using System.Threading;
using SimpleServiceBus.Bus.MessageManagement;
using SimpleServiceBus.Messages.Management.Monitoring;

namespace SimpleServiceBus.Utilities
{
    public abstract class BackgroundServiceBase : IBackgroundService
    {
        private long _activityCounter;
        public long _started;

        public bool IsBusy
        {
            get { return Interlocked.Read(ref _activityCounter) > 0; }
        }

        #region IBackgroundService Members

        public bool IsRunning
        {
            get { return Interlocked.Read(ref _started) > 0; }
        }

        public void Start()
        {
            if (IsRunning) return;

            StartService();
            Interlocked.Exchange(ref _started, 1);
        }

        public void Stop()
        {
            if (!IsRunning) return;

            Interlocked.Exchange(ref _started, 0);

            WaitForIdle();
            ShutDownService();
        }

        public virtual void Dispose()
        {
            Stop();
        }

        #endregion

        public event EventHandler<ErrorEventArgs> ServiceException;

        public void Restart()
        {
            Stop();
            Start();
        }

        protected virtual bool OnServiceException(Exception ex)
        {
            EventHandler<ErrorEventArgs> evt = ServiceException;
            if (evt != null)
            {
                evt(this, new ErrorEventArgs(ex));
                return true;
            }
            return false;
        }

        private void WaitForIdle()
        {
            while (IsBusy)
                Thread.Sleep(10);
        }

        protected abstract void StartService();
        protected abstract void ShutDownService();

        #region Nested type: BusyHandle

        protected class BusyHandle : IDisposable
        {
            private readonly BackgroundServiceBase _service;

            public BusyHandle(BackgroundServiceBase service)
            {
                if (!service.IsRunning)
                    throw new InvalidOperationException(
                        "A service cannot do work unless it is running. Please call Start before performing any runtime operations.");

                _service = service;
                Interlocked.Increment(ref _service._activityCounter);
            }

            #region IDisposable Members

            public void Dispose()
            {
                Interlocked.Decrement(ref _service._activityCounter);
            }

            #endregion
        }

        #endregion
    }
}